package pers.rain.dynamiccompiledemo.holder;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.watch.WatchMonitor;
import cn.hutool.core.io.watch.Watcher;
import cn.hutool.core.util.ReflectUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import pers.rain.dynamiccompiledemo.model.Config;
import pers.rain.dynamiccompiledemo.plugin.IPlugin;

import javax.annotation.PostConstruct;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.util.ArrayList;
import java.util.List;

@Data
@Component
@Slf4j
public class ConfigHolder {
    @Value("${config-file.path}")
    private String configFilePath;
    private Config config;
    private WatchMonitor watchMonitor;

    @PostConstruct
    public void initWatcher() {
        File file = FileUtil.file(configFilePath);
        watchMonitor = WatchMonitor.create(file, WatchMonitor.EVENTS_ALL);
        watchMonitor.setWatcher(new Watcher() {
            @Override
            public void onCreate(WatchEvent<?> event, Path currentPath) {
                log.info("配置文件重新创建：{}-> {}", currentPath, event.context());
                initConfigList();
            }

            @Override
            public void onModify(WatchEvent<?> event, Path currentPath) {
                log.info("配置文件被修改：{}-> {}", currentPath, event.context());
                initConfigList();
            }

            @Override
            public void onDelete(WatchEvent<?> event, Path currentPath) {
                log.info("配置文件被删除：{}-> {}", currentPath, event.context());
                initConfigList();
            }

            @Override
            public void onOverflow(WatchEvent<?> event, Path currentPath) {
            }
        });
        watchMonitor.setMaxDepth(1);
        watchMonitor.start();
        initConfigList();
    }

    public void initConfigList() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        if (!FileUtil.exist(configFilePath)) {
            log.error("配置文件{}不存在", configFilePath);
            return;
        }
        Config newConfig = null;
        try {
            newConfig = objectMapper.readValue(FileUtil.readUtf8String(configFilePath), Config.class);
        } catch (JsonProcessingException e) {
            log.error("解析配置文件异常：{}", e.toString());
            return;
        }
        List<URL> urls = new ArrayList<>();
        for (Config.ConfigItem item : newConfig.getClassList()) {
            String classPath = item.getClassPath();
            try {
                urls.add(new URL("file:" + classPath));
            } catch (MalformedURLException e) {
                log.error("解析classPath异常：{}", e.toString());
                return;
            }
        }
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        URLClassLoader urlClassLoader = new URLClassLoader(urls.toArray(new URL[0]), loader);
        for (Config.ConfigItem item : newConfig.getClassList()) {
            try {
                Class<?> clazz = urlClassLoader.loadClass(item.getClassName());
                IPlugin iPlugin = (IPlugin) ReflectUtil.newInstance(clazz);
                item.setIPlugin(iPlugin);
            } catch (ClassNotFoundException e) {
                log.error("插件类加载异常：{}", e.toString());
                return;
            }
        }
        config = newConfig;
    }
}
